package com.interview.javabase.thread;

import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicStampedReference;

/**
 * @description: 编写一个线程不安全的list 并解决线程不安全现象
 *
 * @author: zhangshuguang@vichain.com
 * @date: 2019/06/14 13:49
 */
public class Demo9 {

    public static void main(String[] args) throws InterruptedException {
        List<String> list = new ArrayList<>();

        System.out.println("===========现象演示==========");
        for (int i = 0; i < 30; i++) {
            new Thread(() -> {
                list.add(UUID.randomUUID().toString().substring(1,8));
                System.out.println(list);
            },"t1").start();
        }

        try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }

        System.out.println("===========解决方案一 Vector==========");

        List<String> list1 = new Vector<>();
        for (int i = 0; i < 30; i++) {
            new Thread(() -> {
                list1.add(UUID.randomUUID().toString().substring(1, 8));
                System.out.println(list1);
            }, "t2").start();
        }

        try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
        System.out.println("===========解决方案二  Collections.synchronizedList==========");

        List<String> list2 = Collections.synchronizedList(new ArrayList<>());
        for (int i = 0; i < 30; i++) {
            new Thread(() -> {
                list2.add(UUID.randomUUID().toString().substring(1, 8));
                System.out.println(list2);
            }, "t2").start();
        }

        try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
        System.out.println("===========解决方案三 CopyOnWriteArrayList()==========");

        /**
         *
         * 写时复制
         *  CopyOnWrite容器即写时复制的容器。往一个容器添加元素的时候，不直接往容器中Object[]添加元素，而且先将容器Object[]进行拷贝，
         *  复制出一个新的容器Object[] newElements，然后往新的容器Object[] newElements里添加元素添加完元素之后，再将原容器的引用指向新的容器，
         *  setArray(newElements)；这样做的好处是可以对CopyOnWrite容器进行并发读，而不需要加锁，因为当前容器不会添加任何元素。
         *  所以CopyOnWrite容器也是一种读写分离的思想。
         *    public boolean add(E e) {
         *         final ReentrantLock lock = this.lock;
         *         lock.lock();
         *         try {
         *             Object[] elements = getArray();
         *             int len = elements.length;
         *             Object[] newElements = Arrays.copyOf(elements, len + 1);
         *             newElements[len] = e;
         *             setArray(newElements);
         *             return true;
         *         } finally {
         *             lock.unlock();
         *         }
         *     }
         */
        List<String> list3 = new CopyOnWriteArrayList();
        for (int i = 0; i < 30; i++) {
            new Thread(() -> {
                list3.add(UUID.randomUUID().toString().substring(1, 8));
                System.out.println(list3);
            }, "t2").start();
        }
    }

}
